<?php
/**
 * @author andery <andery@foxmail.com>
 * @link http://www.minophp.com/
 * @version $Id: CApplication.php 2009-07-25 22:20:11Z andery $
 */
class CApplication {
    private static $_langModel = array();
    private $_apppath;
    private $_controller;
    private $_action;
    
    public function __construct($config=null)
    {
        Nimo::setApp($this);
        $this->initNimoHandlers();
        CConf::getInstance($config);
        $this->setAppPath(CConf::get('system.apppath'));
    }
    
    public function processRequest()
    {
        return new CUrlManager();
    }
    
    public function getAppPath()
    {
        return $this->_apppath;
    }
    
    public function setAppPath($path)
    {
        if (($this->_apppath=realpath($path))===false || !is_dir($this->_apppath)) {
            throw new CException(Nimo::lang('apppath_not_exists'));
        }
    }
    
    public function start()
    {
        // 设定时区
        if(date_default_timezone_get() != CConf::get('system.timezone')) {
            date_default_timezone_set(CConf::get('system.timezone'));
        }
        $route = $this->processRequest();
        $this->controller = $route->getController().'Controller';
        if (!@include_once $this->getAppPath() . '/control/' . $this->controller . '.php'){
            throw new CHttpException(404, Nimo::lang('controller_not_exists'));
        }
        $controller = new $this->controller($route->getParams());
        $this->action = $action = 'on_'.$route->getAction();
        if (!method_exists($this->controller, $this->action)) {
            throw new CHttpException(404, Nimo::lang('action_not_exists'));
        } else {
            $controller->$action();
        }
    }
    
    /**
     * 加载模型
     * @param string 模型名称
     */
    public function loadModel($model)
    {
        if (empty($_ENV[$model])) {
            if (file_exists($this->getAppPath().'/model/'.$model.'Model.php')){
                require_once $this->getAppPath().'/model/'.$model.'Model.php';
                eval('$_ENV[$model] = new '.$model.'Model();');    
                return $_ENV[$model];
            }else{
                throw new CException(Nimo::lang('model_not_exists'));
            }
        } else {
            return $_ENV[$model];
        }
    }
    
    /**
     * 加载应用程序语言包
     * @param string 语句别名alias
     * @param string 模型名称
     */
    public static function lang($alias, $model='default')
    {        
        if(!isset(self::$_langModel[$model])) {
            self::$_langModel[$model] = include_once($this->getAppPath().'/lang/'.$model.'.lang.php');
        }
        if(array_key_exists($alias,self::$_langModel[$model])) {
            return self::$_langModel[$model][$alias];
        } else {
            return $alias;
        }
    }
    
    /**
     * HTML标签转换
     * @param string or array
     * @return string or array
     */
    public function encode($data)
    {
        if(is_array($data)) {
            $d=array();
            foreach($data as $key=>$value) {
                if(is_string($key))
                    $key=htmlspecialchars($key,ENT_QUOTES,CConf::get('system.charset'));
                if(is_string($value))
                    $value=htmlspecialchars($value,ENT_QUOTES,CConf::get('system.charset'));
                else if(is_array($value))
                    $value=self::encode($value);
                $d[$key]=$value;
            }
            return $d;
        } elseif(is_string($data)) {
            return htmlspecialchars($data,ENT_QUOTES,CConf::get('system.charset'));
        } else {
            throw new CException(Nimo::lang('encode_data_type_invalid'));
        }
    }
    
    protected function getSourceLines($file,$line)
    {
        $maxLines=25;
        $line--;
        if ($line<0 || ($lines=@file($file))===false || ($lineCount=count($lines))<=$line) {
            return array();
        }
        
        $halfLines=intval($maxLines/2);
        $beginLine=$line-$halfLines>0?$line-$halfLines:0;
        $endLine=$line+$halfLines<$lineCount?$line+$halfLines:$lineCount-1;

        $sourceLines=array();
        for ($i=$beginLine;$i<=$endLine;++$i) {
            $sourceLines[$i+1]=$lines[$i];
        }
        return $sourceLines;
    }
    
    public function displayException($exception)
    {
        $traceInfo='';
        $trace = $exception->getTrace();
        foreach($trace as $t) {
        	$time = date("y-m-d H:i:m");
            $traceInfo .= '['.$time.'] ';
            $traceInfo .= (!empty($t['file']) && !empty($t['line'])) ? $t['file'] . ' (' . $t['line'] . ') ' : '';
            $traceInfo .= !empty($t['class']) ? $t['class'] : '';
            $traceInfo .= !empty($t['type']) ? $t['type'] : '';
            $traceInfo .= !empty($t['function']) ? $t['function'] : '';
            $traceInfo .= '(';
            if (!empty($t['args'])) {
                foreach ($t['args'] as $ta) {
                    if (is_array($ta)) {
                        $traceInfo .= 'array, ';
                    } else {
                        $traceInfo .= $ta.', ';
                    }
                }
                $traceInfo = substr($traceInfo, 0, -2);    
            }    
            $traceInfo .=")\n";
        }
        $error['code'] = ($exception instanceof CHttpException||$exception instanceof CDbException)?$exception->statusCode:500;
        $error['trace'] = $traceInfo;
        $error['type'] = get_class($exception);
        $error['message'] = $exception->getMessage();
        $error['file'] = $exception->getFile();
        $error['line'] = $exception->getLine();
        $error['source']    = $this->getSourceLines($error['file'],$error['line']);
        if ($exception instanceof CHttpException && !NIMO_DEBUG) {
            include_once(NIMO_PATH.'/view/system/error'.$error['code'].'.php');
        } elseif ($exception instanceof CDbException && !NIMO_DEBUG) {
            $error['db_type'] = $exception->db_type;
        	include_once(NIMO_PATH.'/view/system/db_error.php');
        } else {
            include_once(NIMO_PATH.'/view/system/exception.php');
        }
        die();
    }
    
    public function displayError($code,$message,$file,$line)
    {
        $trace=debug_backtrace();
        if (count($trace)>3) $trace=array_slice($trace,3);
        $traceString='';
        foreach ($trace as $i=>$t) {
            if (!isset($t['file'])) $t['file']='unknown';
            if (!isset($t['line'])) $t['line']=0;
            if (!isset($t['function'])) $t['function']='unknown';
            $traceString.="#$i {$t['file']}({$t['line']}): ";
            if (isset($t['object']) && is_object($t['object'])) $traceString.=get_class($t['object']).'->';
            $traceString.="{$t['function']}()\n";
        }
        $app = Nimo::app();
        if ($app instanceof CApplication) {
            $error=array(
                'code'=>500,
                'type'=>'PHP Error',
                'message'=>$message,
                'file'=>$file,
                'line'=>$line,
                'trace'=>$traceString,
                'source'=>$this->getSourceLines($file,$line),
            );
            if (!headers_sent()) header("HTTP/1.0 500 PHP Error");
            if (NIMO_DEBUG) {
                include_once(NIMO_PATH.'/view/system/exception.php');
            } else {
                include_once(NIMO_PATH.'/view/system/error500.php');
            }
       } else {
            include_once(NIMO_PATH.'/view/system/error.php');
       }
       die();
    }
    
    
    public function handleException($exception)
    {
        $this->restoreHandlers();
        $this->displayException($exception);
    }
    
    public function handleError($code,$message,$file,$line)
    {
        $this->restoreHandlers();
        $this->displayError($code,$message,$file,$line);
    }
    
    /* 恢复默认异常处理 */
    private function restoreHandlers()
    {
        restore_error_handler();
        restore_exception_handler();
    }
    
    /**
     * nimophp自定义异常处理
     */
    private function initNimoHandlers()
    {
        set_exception_handler(array($this,'handleException'));
        set_error_handler(array($this,'handleError'), error_reporting());
    }
}